#ifndef OBJECT_FACTORY_H
#define OBJECT_FACTORY_H

#include <string>
#include <map>
#include <vector>
#include "AbstractObject.h"
#include "Logger.h"

//class AbstractObject;

class Sheet;

class ObjectProto
{
public:
	bool finalized;
	AbstractObject* cppObj;
	std::string name;
	std::string parentName;
	int luaRef;
	bool isPureCpp;
};

class ObjectFactory
{
public:
	ObjectFactory(Sheet* sh);

	AnyStrongAOPtr createObject(std::string className);
	
	// note that these only work before a prototype is finalized
	void setInheritance(std::string className, std::string parentName);
	void setCppInheritanceOnly(std::string className, std::string parentName);
	ObjectProto* getPrototype(std::string className);

	// this doesn't ever have to be explicitely called, since getPrototype
	//  and createObject will automatically call it as necessary
	//  (note that if it already exists, and reload is false, it won't reload
	//   it (but it will still return true))
	bool loadPrototype(std::string className, bool reload = false);

	~ObjectFactory();
private:
	void registerCustomDatatypes();
	int instantiateLuaPart(int luaProto, AbstractObject* cppPart);
	void addCppProto(AbstractObject* cppProto, std::string name);
	Sheet* _sh;
	std::map<std::string, ObjectProto*> _protos;
};

class DataTypeBase
{
public:
	virtual void registerFunctions(lua_State* L)=0;
};

class DataTypeStub
{
public:
	DataTypeBase* dt;
	std::string name;
};

class SingletonCppProtoDepot
{
private:
	SingletonCppProtoDepot()
	{
	}

	static SingletonCppProtoDepot* _theDepot;
	std::vector<AbstractObject*> _protos;
	std::vector<DataTypeStub> _dts;
public:
	static SingletonCppProtoDepot* get()
	{
		if(!_theDepot)
			_theDepot = new SingletonCppProtoDepot;
		return _theDepot;
	}

	void debugListProtos()
	{
		for(unsigned int i = 0; i < _protos.size(); ++i)
			LOG(logINFO) << "CppProto: " << _protos[i]->getCppTypeName();
	}

	void addCppProto(AbstractObject* src)
	{
		_protos.push_back(src);
	}

	void addDatatype(DataTypeBase* dt, std::string name)
	{
		DataTypeStub temp;
		temp.dt = dt;
		temp.name = name;
		_dts.push_back(temp);
	}

	int getNumDatatypes()
	{
		return _dts.size();
	}

	DataTypeStub getDatatype(int i)
	{
		return _dts[i];
	}

	int getNumProto()
	{
		return _protos.size();
	}

	AbstractObject* getProto(int id)
	{
		return _protos[id];
	}
};

template <class T>
class RegisterCustomDatatype
{
public:
	RegisterCustomDatatype(const char* name)
	{
		T* bla = new T;
		std::string tempo(name);
		SingletonCppProtoDepot::get()->addDatatype(bla, tempo);
	}
};

template <class T>
class RegisterCppObj
{
public:
	T* bla;
	RegisterCppObj()
	{
		bla = new T;
		std::cout << "RCPPO: " << bla->getCppTypeName() << std::endl;
		//LOG(logDEBUG) << "RCPPO: " << bla->getCppTypeName();
		SingletonCppProtoDepot::get()->addCppProto(bla);
	}
};

#endif
